package org.apache.commons.graph.export

import com.gitee.wsl.ext.base.currentTime
import com.gitee.wsl.text.format.format
import kotlinx.io.IOException
import kotlinx.io.Sink
import org.apache.commons.graph.Graph
import org.apache.commons.graph.Mapper
import org.apache.commons.graph.utils.Assertions
import kotlin.jvm.JvmField
import kotlin.reflect.KClass
import kotlin.time.ExperimentalTime

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

abstract class AbstractExporter<V, E, T : AbstractExporter<V, E, T>>(
    @JvmField protected val graph: Graph<V, E>,
    name: String?
) {

    private val vertexProperties: MutableMap<String, Mapper<V, *>> = HashMap()

    private val edgeProperties: MutableMap<String, Mapper<E, *>> = HashMap()

    private val name: String = name ?: G

    protected var writer: Sink? = null
        private set

    protected fun addEdgeProperty(propertyName: String, edgeProperty: Mapper<E, *>) {
        edgeProperties[propertyName] = edgeProperty
    }

    protected fun addVertexProperty(propertyName: String, vertexProperty: Mapper<V, *>) {
        vertexProperties[propertyName] = vertexProperty
    }

    @Throws(Exception::class)
    protected abstract fun comment(text: String)

    @Throws(Exception::class)
    protected abstract fun edge(edge: E, head: V, tail: V, properties: Map<String, Any>?)

    @Throws(Exception::class)
    protected abstract fun endGraph()

    @Throws(Exception::class)
    protected abstract fun endSerialization()

    @Throws(Exception::class)
    protected abstract fun enlistEdgesProperty(name: String?, type: KClass<*>?)

    @Throws(Exception::class)
    protected abstract fun enlistVerticesProperty(name: String?, type: KClass<*>?)

    @Throws(Exception::class)
    protected abstract fun startGraph(name: String?)

    @Throws(Exception::class)
    protected abstract fun startSerialization()

//    @Throws(GraphExportException::class)
//    fun to(outputFile: File) {
//        try {
//            to(FileOutputStream(Assertions.checkNotNull(outputFile, "Impossibe to export the graph in a null file")))
//        } catch (e: FileNotFoundException) {
//            throw RuntimeException(e)
//        }
//    }
//
//    @Throws(GraphExportException::class)
//    fun to(outputStream: OutputStream) {
//        to(OutputStreamWriter(Assertions.checkNotNull(outputStream, "Impossibe to export the graph in a null stream")))
//    }

    @OptIn(ExperimentalTime::class)
    @Throws(GraphExportException::class)
    fun to(writer: Sink) {
        this.writer = Assertions.checkNotNull(writer, "Impossibe to export the graph in a null stream")

        try {
            startSerialization()
            comment(String.format("Graph generated by Apache Commons Graph on %s%n", currentTime))

            startGraph(name)


            // this is basically for the GraphML


            // END
            for (vertex in graph.vertices) {
                val properties: MutableMap<String, Any> = HashMap(vertexProperties.size)

                for ((key, value) in vertexProperties) {
                    properties[key] = value.map(vertex)!!
                }

                vertex(vertex, properties)
            }

            for (edge in graph.edges) {
                val vertices = graph.getVertices(edge)

                val properties: MutableMap<String, Any> = HashMap(edgeProperties.size)

                for ((key, value) in edgeProperties) {
                    properties[key] = value.map(edge)!!
                }

                edge(edge, vertices!!.head, vertices.tail, properties)
            }

            endGraph()

            endSerialization()
        } catch (e: Exception) {
            throw GraphExportException(
                e, "an error occurred while exporting graph %s (named %s) to writer %s",
                graph,
                name,
                writer
            )
        } finally {
            try {
                writer.close()
            } catch (e: IOException) {
                // swallow it
            }
        }
    }

    @Throws(Exception::class)
    protected abstract fun vertex(vertex: V, properties: Map<String, Any>?)

    companion object {
        private const val G = "G"
    }
}
